package il.technion.cs236369.webserver.typeHandlers;

import static il.technion.cs236369.webserver.RequestHandler.generateErrorResponse;
import static il.technion.cs236369.webserver.RequestHandler.generateResponse;
import il.technion.cs236369.common.Common;
import il.technion.cs236369.exceptions.ParsingException;
import il.technion.cs236369.interfaces.ICookie;
import il.technion.cs236369.interfaces.ISession;
import il.technion.cs236369.interfaces.ITSPWriter;
import il.technion.cs236369.parsers.TSPParser;
import il.technion.cs236369.webserver.Request;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.tools.ToolProvider;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.entity.StringEntity;

/**
 * A TSP writing engine Will handle http requests for tsp pages and return the
 * correct response
 * 
 * @author Gal Lalouche
 * 
 */
public class TSPEngine extends TypeHandlerImpl {
	private final TSPParser	parser;

	/**
	 * Creates a new TSP engine
	 * 
	 * @param props The properties for the handler
	 */
	public TSPEngine(Properties props) {
		super(props);

		String jrePath = props.getProperty("jre_path");
		if (jrePath != null) {
			System.setProperty("java.home", jrePath);
			Common.getOut().println("using non-default jrePath");
		} else {
			Common.getOut().println("using default compiler");
		}
		parser = new TSPParser(ToolProvider.getSystemJavaCompiler(), ToolProvider.getSystemJavaCompiler().getStandardFileManager(null, null, null));
	}

	@Override
	public HttpResponse handle(Request request) {
		Map<String, String> params = new HashMap<String, String>();

		// get params
		for (NameValuePair entry : request.getQueryParams()) {
			params.put(entry.getName(), entry.getValue());
		}

		ITSPWriter writer = null;
		try {
			// parse file and return response
			writer = parser.parse(new File(request.getAbsolutePath()));
			ISession session = getSession(request);
			return getResponse(params, writer, session);
		} catch (FileNotFoundException e) {
			return generateErrorResponse(404, "Not Found", "could not find requested tsp file");
		} catch (ParsingException e) {
			return generateErrorResponse(500, "Internal Server Error", "an error occured while parsing the TSP file");
		} catch (UnsupportedEncodingException e) {
			return handleUnsupportedEncoding();
		} catch (NullPointerException e) {
			return setCookieForClientAndRetry(params, writer);
		} catch (IllegalArgumentException e) {
			return generateErrorResponse(400, "Bad Request",
					"unrecognized or invalidated cookie; please delete your cookies associated with the website");
		} finally {
			deleteClassFiles();
		}
	}

	private HttpResponse setCookieForClientAndRetry(Map<String, String> params, ITSPWriter writer) {
		Common.getOut().println("NPE thrown, generating new cookie");
		ICookie newCookie = cm.generateNewCookie();
		ISession session = cm.getSession(newCookie);
		try {
			assert writer != null;
			HttpResponse response = getResponse(params, writer, session);
			response.setHeader("Set-Cookie", newCookie.getName() + "=" + newCookie.getValue());
			return response;
		} catch (UnsupportedEncodingException e1) {
			return handleUnsupportedEncoding();
		}
	}

	private HttpResponse handleUnsupportedEncoding() {
		return generateErrorResponse(500, "Internal Server Error", "an error occured while creating the TSP file");
	}

	private HttpResponse getResponse(Map<String, String> params, ITSPWriter writer, ISession session) throws UnsupportedEncodingException {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		PrintStream ps = new PrintStream(baos);
		writer.writeTSP(params, ps, session);
		String result = baos.toString();
		StringEntity strEntity = new StringEntity(result);
		strEntity.setContentType("text/html");
		return generateResponse(200, "OK", strEntity);
	}

	// deletes all class files created by the parser
	private static void deleteClassFiles() {
		File classDir = new File("./bin");
		for (String fileName : classDir.list(new FilenameFilter() {

			@Override
			public boolean accept(File dir, String name) {
				return name.startsWith(TSPParser.CLASS_NAME);
			}
		})) {
			File file = new File(classDir, fileName);
			file.delete();
		}
	}
}
